package org.hang.gift.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.hang.exception.BusinessException;
import org.hang.exception.ResponseCode;
import org.hang.exception.RonghuaAssert;
import org.hang.gift.clients.GiftClient;
import org.hang.gift.constants.ShopCarConstants;
import org.hang.gift.domain.GoodsSku;
import org.hang.gift.domain.ShopCar;
import org.hang.gift.dto.AddShopCarDTO;
import org.hang.gift.dto.UpdateNumDTO;
import org.hang.gift.dto.UpdateSelectDTO;
import org.hang.gift.mapper.ShopCarMapper;
import org.hang.gift.service.IShopCarService;
import org.hang.gift.strategy.ShopCarDataSyncKeyGroupStrategy;
import org.hang.gift.strategy.ShopCarDataSyncKeyGroupStrategyFactory;
import org.hang.util.AjaxResult;
import org.hang.util.TenantIdName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hang
 * @since 2024-06-11
 */
@Service
public class ShopCarServiceImpl extends ServiceImpl<ShopCarMapper, ShopCar> implements IShopCarService {
    @Autowired
    private GiftClient giftClient;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Value("${hang.dateSync.groupStrategy}")
    private String groupStrategy;

    @Override
    public void addShopCar(AddShopCarDTO addShopCarDTO) {
        //参数非空校验
        RonghuaAssert.isNotNull(addShopCarDTO.getSkuId(), ResponseCode.RESPONSE_CODE_500);
        //查询当前用户有没有购物车数据
        String key = ShopCarConstants.SHOP_CAR_REDIS_PREFIX + addShopCarDTO.getUserId();
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(key);
        List<String> values = hashOps.values();
        if (CollectionUtil.isEmpty(values)) {
            //在redis中没有购物车数据，就查询判断当前用户在数据库中有没有购物车数据
            List<ShopCar> shopCars = selectList(new EntityWrapper<ShopCar>().eq("user_id", addShopCarDTO.getUserId()));
            //数据库中有购物车数据，就遍历购物车数据，判断是否购物车数据包含当前商品, 修改后的数量 = 原数量 + 本次要购买的量
            if (CollectionUtil.isNotEmpty(shopCars)) {
                Boolean flag = false;
                for (ShopCar shopCar : shopCars) {
                    // 包含，就修改数量后放入redis，修改后的数量 = 原数量 + 本次要购买的量
                    if (addShopCarDTO.getSkuId().equals(shopCar.getSkuId())) {
                        shopCar.setNum(shopCar.getNum() + addShopCarDTO.getNum());
                        shopCar.setUpdateTime(new Date());
                        flag = true;
                    }
                    //如果sku存在就修改数量后放入redis,如果不存在,本身数据库也要放入redis
                    hashOps.put(shopCar.getSkuId() + "", JSONObject.toJSONString(shopCar));
                }
                //不存在的,就要根据SkuId构建购物车对象,放入Redis
                if (!flag) {
                    ShopCar shopCar = getShopCarBySkuId(addShopCarDTO);
                    hashOps.put(shopCar.getSkuId() + "", JSONObject.toJSONString(shopCar));
                }
            } else {
                //redis和数据库都为空,直接构建购物车对象,存入redis
                ShopCar shopCar = getShopCarBySkuId(addShopCarDTO);
                hashOps.put(shopCar.getSkuId() + "", JSONObject.toJSONString(shopCar));
            }
        } else {
            //redis中有购物车数据，判断是否购物车数据包含当前商品
            Boolean isBoolean = hashOps.hasKey(addShopCarDTO.getSkuId() + "");
            ShopCar shopCar = null;
            if (isBoolean) {
                //包含，判断isDelete后再修改数量后放入redis
                String str = hashOps.get(addShopCarDTO.getSkuId() + "");
                shopCar = JSONObject.parseObject(str, ShopCar.class);
                if (shopCar.getIsDeleted() == 1) {
                    //isDeleted=1,修改isDelete=0,并修改数量=本次要购买的量
                    shopCar = getShopCarBySkuId(addShopCarDTO);
                } else {
                    //isDeleted=0,并修改数量=原数量+本次要新增的数量
                    shopCar.setNum(shopCar.getNum() + addShopCarDTO.getNum());
                }
            } else {
                // 不包含，就要根据SkuId构建购物车对象,放入Redis
                shopCar = getShopCarBySkuId(addShopCarDTO);
            }
            hashOps.put(addShopCarDTO.getSkuId() + "", JSONObject.toJSONString(shopCar));
        }
    }

    /**
     * 修改数量
     * 参数：skuid ，要修改的数量 num
     *
     * @param updateNumDTO
     */
    @Override
    public void updateNum(UpdateNumDTO updateNumDTO) {
        BoundHashOperations<String, String, String> hashOps =
                redisTemplate.boundHashOps(ShopCarConstants.SHOP_CAR_REDIS_PREFIX + updateNumDTO.getUserId());
        //因为要修改就要进入购物车页面，所以都会先查询，再修改的，所以这个地方没有对 hashOps 是否为空作判断
        String s = hashOps.get(updateNumDTO.getSkuId().toString());
        //判断是否为空 做一个断言
        RonghuaAssert.isNotNull(s, ResponseCode.RESPONSE_CODE_500);
        ShopCar shopCar = JSONObject.parseObject(s, ShopCar.class);
        //修改数量
        shopCar.setNum(updateNumDTO.getNum());
        shopCar.setUpdateTime(new Date());
        // 重新存入redis 中
        hashOps.put(shopCar.getSkuId().toString(), JSONObject.toJSONString(shopCar));
        //更新过期时间
        hashOps.expire(26, TimeUnit.HOURS);
    }

    /**
     * 修改状态 参数：skuid ，状态 selected
     *
     * @param updateSelectDTO
     */
    @Override
    public void updateSelect(UpdateSelectDTO updateSelectDTO) {
        BoundHashOperations<String, String, String> hashOps =
                redisTemplate.boundHashOps(ShopCarConstants.SHOP_CAR_REDIS_PREFIX + updateSelectDTO.getUserId());
        String s = hashOps.get(updateSelectDTO.getSkuId().toString());
        //判断是否为空 做一个断言
        RonghuaAssert.isNotNull(s, ResponseCode.RESPONSE_CODE_500);
        ShopCar shopCar = JSONObject.parseObject(s, ShopCar.class);
        //修改状态
        shopCar.setSelect(updateSelectDTO.getSelected());
        shopCar.setUpdateTime(new Date());
        // 重新存入redis 中
        hashOps.put(shopCar.getSkuId().toString(), JSONObject.toJSONString(shopCar));
        //更新过期时间
        hashOps.expire(26, TimeUnit.HOURS);
    }

    /**
     * @param addShopCarDTO
     * @return
     */
    private ShopCar getShopCarBySkuId(AddShopCarDTO addShopCarDTO) {
        // 4.1 远程调用礼物服务,根据skuId获取商品的详情信息
        AjaxResult ajaxResult = giftClient.get(addShopCarDTO.getSkuId());
        if (!ajaxResult.isSuccess()) {
            throw new BusinessException(ajaxResult.getMessage());
        }
        String s = JSONObject.toJSONString(ajaxResult.getResultObj());
        GoodsSku goodsSku = JSONObject.parseObject(s, GoodsSku.class);
        // 4.2 根据商品信息构建购物车对象
        ShopCar car = new ShopCar();
        car.setSpuId(goodsSku.getSpuId());
        car.setSpuName(goodsSku.getSpuName());
        car.setSkuId(goodsSku.getId());
        car.setSkuInfo(goodsSku.getSkuName());
        car.setAddPrice(goodsSku.getPrice());
        car.setPrice(goodsSku.getPrice());
        car.setUserId(addShopCarDTO.getUserId());
        car.setUsername(addShopCarDTO.getUsername());
        car.setTenantId(goodsSku.getTenantId());
        car.setTenantName(goodsSku.getTenantId() + "");
        car.setSelect(true);    // 在新增的时候,状态默认就是选中的
        car.setCreateTime(new Date());
        car.setNum(addShopCarDTO.getNum());
        return car;
    }

    /**
     * 购物车分组展示
     * @return
     */
    @Override
    public Map<TenantIdName, List<ShopCar>> listGroup(Long Id) {
        // 1,先从redis中查询当前登录用户的购物车数据
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(ShopCarConstants.SHOP_CAR_REDIS_PREFIX + Id);
        //获取所有的 json字符串格式的 shapcar 对象
        List<String> values = hashOps.values();
        List<ShopCar> shopCars = null;
        //2,如果redis存在数据,过滤掉isDeleted=1的数据,并转成Map后返回
        if (CollectionUtil.isNotEmpty(values)) {
            shopCars = values.stream().map(x -> JSONObject.parseObject(x, ShopCar.class))
                    .filter(y -> y.getIsDeleted() == 0).collect(Collectors.toList());
        } else {
            // 3,如果redis没有数据,从数据库中查询
            shopCars = selectList(new EntityWrapper<ShopCar>().eq("user_id", Id));
            // 4,将数据放入redis
            shopCars.forEach(x -> {
                hashOps.put(x.getSkuId().toString(), JSONObject.toJSONString(x));
            });
        }
        //更新过期时间
        hashOps.expire(26, TimeUnit.HOURS);
        // 5,将数据根据店铺信息分组，并转成Map后返回
        Map<TenantIdName, List<ShopCar>> map = shopCars.stream()
                .collect(Collectors.groupingBy(x -> new TenantIdName(x.getTenantId(), x.getTenantName())));
        return map;
    }

    /**
     * 购物车数据同步
     */
    @Override
    public void syncData() {
        // 1 获取redis中所有用户的购物车key - 300w   shop_car_*
        Set<String> keys = redisTemplate.keys(ShopCarConstants.SHOP_CAR_REDIS_PREFIX + "*");
        // 2 对这些key通过策略模式来做分组
        ShopCarDataSyncKeyGroupStrategy strategy = ShopCarDataSyncKeyGroupStrategyFactory.getStrategy(this.groupStrategy);
        Map<Integer, List<String>> listMap = strategy.group(keys);
        //3.循环分组，分批次同步购物车数据，获取迭代对象，通过迭代对象就可以获取 entry节点
        Iterator<Map.Entry<Integer, List<String>>> iterator = listMap.entrySet().iterator();
        while (iterator.hasNext()) {
            //获取entry节点
            Map.Entry<Integer, List<String>> next = iterator.next();
            List<String> list =next.getValue();//获取每一组中的大key的集合
            List<Long> groupDelIds = new ArrayList<>();//一组中所有要删除的购物车数据的id
            List<ShopCar> insertOrupdate = new ArrayList<>(); //一组中所有要新增或修改的购物车数据
            for (String key:list) {
                //key 为 每一个 shop_car_userId
                BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(key);
                List<String> values = hashOps.values();
                List<ShopCar> shopCars =
                        values.stream().map(x -> JSONObject.parseObject(x, ShopCar.class)).collect(Collectors.toList());
                //删除:过滤出所有isdeleted = 1 的数据
                List<Long> delIds = shopCars.stream().filter(x -> x.getIsDeleted() == 1).map(ShopCar::getId).collect(Collectors.toList());
                // 加入删除集合中
                groupDelIds.addAll(delIds);
                //除删除外，剩下的数据就都是新增 和 修改的了。
                List<ShopCar> carList = shopCars.stream().filter(x -> x.getIsDeleted() == 0).collect(Collectors.toList());
                //加入 新增/修改集合中
                insertOrupdate.addAll(carList);
            }
            //删除一组中 在 redis中被删除的购物车数据
            if(CollectionUtil.isNotEmpty(groupDelIds)){
                super.deleteBatchIds(groupDelIds);
            }
            //新增 修改
            if(CollectionUtil.isNotEmpty(insertOrupdate)){
                super.insertOrUpdateBatch(insertOrupdate);
            }
        }
    }
}


